import Cesium, { Cartesian3, Color, Event, JulianDate, PointPrimitiveCollection, SampledProperty, SimplePolylineGeometry } from '../../Ces/Cesium'
import { We_TypeValueAndColorRamp } from '../../Weather/WeatherVolumeTransfunctions'
import WeTyphoonCircleImagePrimitive from './WeTyphoonCircleImagePrimitive'
import WeTyphoonCircleWindPrimitive from './WeTyphoonCircleWindPrimitive'

export type WeTyphoonTrackLayerOptions = {
  valueAndColorRamp?: We_TypeValueAndColorRamp
  positions: Cartesian3[]
  colors?: Color[]
  times?: JulianDate[]
  lineWidth?: number
  lineColor?: Color,
  pointSize?: number
  pointOutlineWidth?: number
  pointOutlineColor?: Color
  circleImage?: | undefined | null
  circleRadius?: number
  circleRotationFactor?: number
}

class WeTyphoonTrackLayer {
  public id: string
  private _circleWindPrimitive: WeTyphoonCircleWindPrimitive | undefined | null
  private _circleImagePrimitive: WeTyphoonCircleImagePrimitive | undefined | null
  private simplePolylineGeometry: SimplePolylineGeometry
  private pointPrimitiveCollection: PointPrimitiveCollection

  private dirty = false
  private positions: Cartesian3[]
  private colors: Color[]
  private times: JulianDate[]
  private pointSize: number
  private pointOutlineColor: Color
  private pointOutlineWidth: number
  private lineWidth: number
  private lineColor: Color

  private indexSampledProperty: SampledProperty
  private positionSampledProperty: SampledProperty
  private _currentIndex = -1
  private _currentPosition = new Cesium.Cartesian3()
  private _indexChangeEvent: Event

  private circleImage: HTMLImageElement | undefined | null
  private circleRadius: number
  private circleRotationFactor: number

  constructor(options: WeTyphoonTrackLayerOptions) {
    this.lineWidth = options.lineWidth ?? 1
    this.lineColor = options.lineColor ?? Cesium.Color.WHITE
    this.pointSize = options.pointSize ?? 6
    this.pointOutlineWidth = options.pointOutlineWidth ?? 1
    this.pointOutlineColor = options.pointOutlineColor ?? Cesium.Color.BLACK
    this.positions = options.positions
    this.colors = options.colors ?? []
    this.times = options.times ?? []

    this.circleImage = options.circleImage
    this.circleRadius = options.circleRadius ?? 40000
    this.circleRotationFactor = options.circleRotationFactor ?? 1
    this._circleWindPrimitive = new WeTyphoonCircleWindPrimitive()

    this.dirty = true
  }

  get circlePrimitive(): WeTyphoonCircleWindPrimitive {
    return this._circleWindPrimitive as WeTyphoonCircleWindPrimitive
  }

  get indexChangeEvent() {
    if (!this._indexChangeEvent) this._indexChangeEvent = new Cesium.Event()
    return this._indexChangeEvent
  }

  rebuild() {
    const { positions, lineWidth, colors, lineColor } = this
    if (colors.length !== positions.length) {
      colors.length = 0
      positions.forEach(() => {
        colors.push(lineColor)
      })
    }
    //
    //@ts-ignore
    this.simplePolylineGeometry?.destroy()
    this.simplePolylineGeometry = new Cesium.Primitive({
      geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.PolylineGeometry({
          positions,
          colors,
          width: lineWidth,
        }),
      }),
      appearance: new Cesium.PolylineColorAppearance({
        translucent: false,
      }),
      asynchronous: true,
    })

    //
    const { pointSize, pointOutlineColor, pointOutlineWidth } = this
    this.pointPrimitiveCollection?.destroy()
    this.pointPrimitiveCollection = new Cesium.PointPrimitiveCollection()
    //@ts-ignore
    for (const [i, position] of positions.entries()) {
      this.pointPrimitiveCollection.add({
        position,
        color: colors[i],
        pixelSize: pointSize,
        outlineColor: pointOutlineColor,
        outlineWidth: pointOutlineWidth,
      })
    }

    //
    this.indexSampledProperty = undefined
    this.positionSampledProperty = undefined

    const { times } = this
    if (!times) return
    const indexSamplerProperty = new Cesium.SampledProperty(Number)
    const positionSamplerProperty = new Cesium.SampledPositionProperty()
    for (let i = 0; i < times.length; i++) {
      positionSamplerProperty.addSample(times[i], positions[i])
      indexSamplerProperty.addSample(times[i], i)
    }
    positionSamplerProperty.backwardExtrapolationType = Cesium.ExtrapolationType.HOLD
    positionSamplerProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD
    indexSamplerProperty.backwardExtrapolationType = Cesium.ExtrapolationType.HOLD
    indexSamplerProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD

    this.indexSampledProperty = indexSamplerProperty
    this.positionSampledProperty = positionSamplerProperty

    //
    if (this.circleImage) {
      this._circleImagePrimitive = new WeTyphoonCircleImagePrimitive({
        image: this.circleImage,
        rotationFactor: this.circleRotationFactor
      })
      this.circleImage = undefined
    }
  }

  update(frameState) {
    if (!frameState.passes.render) return

    if (this.dirty) {
      this.dirty = false
      this.rebuild()
    }

    //@ts-ignore
    this.simplePolylineGeometry?.update(frameState)
    //@ts-ignore
    if (!this.simplePolylineGeometry?.ready) return
    //@ts-ignore
    this.pointPrimitiveCollection?.update(frameState)

    if (!this.indexSampledProperty) return
    const time = frameState.time

    if (this._indexChangeEvent) {
      const index = Math.floor(this.indexSampledProperty.getValue(time) ?? 0)
      if (this._currentIndex !== index) {
        this._currentIndex = index
        this._indexChangeEvent.raiseEvent(this, index)
      }
    }

    if (!this._circleWindPrimitive) return
    const position = this.positionSampledProperty.getValue(time, this._currentPosition)
    if (!position) {
      return
    }

    if (this._circleWindPrimitive) {
      this._circleWindPrimitive.position = position
      this._circleWindPrimitive.radius = this.circleRadius
      this._circleWindPrimitive.update(frameState)
    }

    if (this._circleImagePrimitive) {
      this._circleImagePrimitive.position = position
      this._circleImagePrimitive.radius = this.circleRadius
      this._circleImagePrimitive.update(frameState)
    }
  }

  isDestroyed() { return false }

  destroy() {
    //@ts-ignore
    this.simplePolylineGeometry = this.simplePolylineGeometry?.destroy()
    //@ts-ignore
    this.pointPrimitiveCollection = this.pointPrimitiveCollection?.destroy()

    this._circleWindPrimitive = this._circleWindPrimitive?.destroy()
    this._circleImagePrimitive = this._circleImagePrimitive?.destroy()

    return Cesium.destroyObject(this)
  }
}

export default WeTyphoonTrackLayer
